package com.proxyforgame.lifesim;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class LifeSim extends JFrame implements Runnable {

	private static final long	serialVersionUID	= 3400265056061021539L;

	private LifeModel			model				= null;
	private LifeView			view				= null;
	private JButton				button1				= null;
	private JButton				button2				= null;
	private JSlider				slider				= null;

	// Simulation runs in a separate thread.
	private volatile Thread		simThread			= null;
	/**
	 * Delay in ms between simulation steps.
	 */
	private int					updateDelay			= 100;

	public LifeSim(String title) {
		super(title);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setResizable(false);

		model = new LifeModel(100, 70);
		view = new LifeView(model, 8, 1);
		add(view);

		JToolBar toolBar = new JToolBar();
		toolBar.setFloatable(false);
		add(toolBar, BorderLayout.NORTH);

		button1 = new JButton("Start");
		toolBar.add(button1);
		button2 = new JButton("Clear field");
		toolBar.add(button2);

		// slider adjusting simulation rate (delay in ms between simulation steps)
		slider = new JSlider(1, 200);
		slider.setValue(50);
		updateDelay = slider.getValue();
		slider.addChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(ChangeEvent e) {
				updateDelay = slider.getValue();
			}
		});

		toolBar.addSeparator();
		toolBar.add(new JLabel(" Fast"));
		toolBar.add(slider);
		toolBar.add(new JLabel("Slow"));

		// Start\Stop button
		button1.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				if (isSimulating()) {
					stopSimulation();
					button1.setText("Start");
				} else {
					startSimulation();
					button1.setText("Stop");
				}
			}
		});
		// Clear field button
		button2.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				synchronized (model) {
					model.clear();
					view.repaint();
				}
			}
		});
		button1.setMaximumSize(new Dimension(100, 50));
		button2.setMaximumSize(new Dimension(100, 50));
		slider.setMaximumSize(new Dimension(300, 50));
		pack();
		setVisible(true);
	}

	/**
	 * Starts simulation.
	 */
	public void startSimulation() {
		if (simThread == null) {
			simThread = new Thread(this);
			simThread.start();
		}
	}

	/**
	 * Stops simulation.
	 */
	public void stopSimulation() {
		simThread = null;
	}

	/**
	 * Checks whether simulation is running.
	 * 
	 * @return true if running
	 */
	public boolean isSimulating() {
		return simThread != null;
	}

	@Override
	public void run() {
		repaint();
		while (simThread != null) {
			try {
				Thread.sleep(updateDelay);
			} catch (InterruptedException e) {
				stopSimulation();
			}
			// Synchronization is used in order to avoid repainting of the field that is being changed.
			synchronized (model) {
				model.simulate();
			}
			repaint();
		}
		repaint();
	}

	public static void main(String[] args) {
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (Exception e) {}

		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				new LifeSim("LifeSim");
			}
		});
	}

}
